﻿using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;

namespace Sidvall.Security
{
    public class AuthorizationManager
    {
        #region Public Members

        public Sidvall.Security.IAuthorizationProvider AuthorizationProvider { get; set; }
        #region DataPortalContextManager

        private Sidvall.Business.DataPortalContextManager _DataPortalContextManager;
        public Sidvall.Business.DataPortalContextManager DataPortalContextManager
        {
            get
            {
                return _DataPortalContextManager;
            }
            set
            {
                _DataPortalContextManager = value;
            }
        }

        #endregion
        #region PrincipalManager

        private PrincipalManager _PrincipalManager;
        public PrincipalManager PrincipalManager
        {
            get
            {
                if (_PrincipalManager != null)
                    return _PrincipalManager;
                return Sidvall.SystemContext.Current.PrincipalManager;
            }
            set
            {
                _PrincipalManager = value;
            }
        }

        #endregion

        #region EnsureIsAuthorized

        public AuthorizeResult EnsureIsAuthorized(string resource)
        {
            return EnsureIsAuthorized(this.PrincipalManager.CurrentPrincipal, resource);
        }
        public AuthorizeResult EnsureIsAuthorized(string resource, string action)
        {
            return EnsureIsAuthorized(this.PrincipalManager.CurrentPrincipal, resource, action);
        }
        public AuthorizeResult EnsureIsAuthorized(IEnumerable<string> resources)
        {
            return EnsureIsAuthorized(this.PrincipalManager.CurrentPrincipal, resources);
        }
        public AuthorizeResult EnsureIsAuthorized(IEnumerable<string> resources, string action)
        {
            return EnsureIsAuthorized(this.PrincipalManager.CurrentPrincipal, resources, action);
        }
        public AuthorizeResult EnsureIsAuthorized(IEnumerable<string> resources, IEnumerable<string> actions)
        {
            return EnsureIsAuthorized(this.PrincipalManager.CurrentPrincipal, resources, actions);
        }

        public AuthorizeResult EnsureIsAuthorized(IPrincipal principal, string resource)
        {
            return EnsureIsAuthorized(principal, new string[] { resource }, new string[] { string.Empty });
        }
        public AuthorizeResult EnsureIsAuthorized(IPrincipal principal, string resource, string action)
        {
            return EnsureIsAuthorized(principal, new string[] { resource }, new string[] { action });
        }
        public AuthorizeResult EnsureIsAuthorized(IPrincipal principal, IEnumerable<string> resources)
        {
            return EnsureIsAuthorized(principal, resources, new string[] { string.Empty });
        }
        public AuthorizeResult EnsureIsAuthorized(IPrincipal principal, IEnumerable<string> resources, string action)
        {
            return EnsureIsAuthorized(principal, resources, new string[] { action });
        }
        public AuthorizeResult EnsureIsAuthorized(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions)
        {
            var items = ResourceItemCollection.CreateResourceItems(resources, actions, null);
            return EnsureIsAuthorized(principal, items);
        }
        public AuthorizeResult EnsureIsAuthorized(ResourceItemCollection items)
        {
            return EnsureIsAuthorized(this.PrincipalManager.CurrentPrincipal, items);
        }
        public AuthorizeResult EnsureIsAuthorized(IPrincipal principal, ResourceItemCollection items)
        {
            var result = GetAuthorizeResult(principal, items);
            if (!result.IsAuthorized)
            {
                if (!string.IsNullOrWhiteSpace(result?.ValidationResult?.ReasonPhrase))
                    throw new Sidvall.Security.UnauthorizedException(result.ValidationResult.ReasonPhrase);
                throw new Sidvall.Security.UnauthorizedException();
            }
            return result;
        }

        #endregion
        #region EnsureIsAuthorizedAsync

        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(Sidvall.Business.IDataPortalContext context)
        {
            if (context == null)
                throw new System.ArgumentNullException(nameof(context));

            this.DataPortalContextManager.EnsureIsValid(context);
            if (context.AffectedResources.Count == 0)
                context.InitializeAffectedResources(null);
            var result = await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, context.AffectedResources).ConfigureAwait(false);
            context.ObjectParameters.Merge(result.ValidationResult.ObjectParameters);
            return result;
        }

        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(string resource, string action, string subAction)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, new string[] { resource }, new string[] { action }, new string[] { string.Empty });
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IEnumerable<string> resources, string action, string subAction)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, new string[] { action }, new string[] { subAction });
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, actions, subActions);
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, string resource, string action, string subAction)
        {
            return await EnsureIsAuthorizedAsync(principal, new string[] { resource }, new string[] { action }, new string[] { string.Empty });
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, string action, string subAction)
        {
            return await EnsureIsAuthorizedAsync(principal, resources, new string[] { action }, new string[] { subAction });
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await EnsureIsAuthorizedAsync(principal, resources, actions, subActions, null);
        }

        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(string resource, string action, string subAction, string resourceItemId)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resource, action, subAction, resourceItemId);
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IEnumerable<string> resources, string action, string subAction, string resourceItemId)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, action, subAction, resourceItemId);
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions, IEnumerable<string> resourceItemIds)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, actions, subActions, resourceItemIds);
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, string resource, string action, string subAction, string resourceItemId)
        {
            return await EnsureIsAuthorizedAsync(principal, new string[] { resource }, new string[] { action }, new string[] { subAction }, new string[] { resourceItemId });
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, string action, string subAction, string resourceItemId)
        {
            return await EnsureIsAuthorizedAsync(principal, resources, new string[] { action }, new string[] { subAction }, new string[] { resourceItemId });
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions, IEnumerable<string> resourceItemIds)
        {
            var items = ResourceItemCollection.CreateResourceItems(resources, actions, subActions, resourceItemIds);
            return await EnsureIsAuthorizedAsync(principal, items);
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(ResourceItemCollection items)
        {
            return await EnsureIsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, items);
        }
        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(IPrincipal principal, ResourceItemCollection items)
        {
            var result = await GetAuthorizeResultAsync(principal, items);
            if (!result.IsAuthorized)
            {
                if (!string.IsNullOrWhiteSpace(result?.ValidationResult?.ReasonPhrase))
                    throw new Sidvall.Security.UnauthorizedException(result.ValidationResult.ReasonPhrase);
                throw new Sidvall.Security.UnauthorizedException();
            }
            return result;
        }

        #endregion
        #region GetAuthorizeResult

        public AuthorizeResult GetAuthorizeResult(string resource)
        {
            return GetAuthorizeResult(this.PrincipalManager.CurrentPrincipal, resource);
        }
        public AuthorizeResult GetAuthorizeResult(string resource, string action)
        {
            return GetAuthorizeResult(this.PrincipalManager.CurrentPrincipal, resource, action);
        }
        public AuthorizeResult GetAuthorizeResult(IEnumerable<string> resources)
        {
            return GetAuthorizeResult(this.PrincipalManager.CurrentPrincipal, resources);
        }
        public AuthorizeResult GetAuthorizeResult(IEnumerable<string> resources, string action)
        {
            return GetAuthorizeResult(this.PrincipalManager.CurrentPrincipal, resources, action);
        }
        public AuthorizeResult GetAuthorizeResult(IEnumerable<string> resources, IEnumerable<string> actions)
        {
            return GetAuthorizeResult(this.PrincipalManager.CurrentPrincipal, resources, actions);
        }

        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, string resource)
        {
            return GetAuthorizeResult(principal, new string[] { resource }, new string[] { string.Empty });
        }
        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, string resource, string action)
        {
            return GetAuthorizeResult(principal, new string[] { resource }, new string[] { action });
        }
        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, IEnumerable<string> resources)
        {
            return GetAuthorizeResult(principal, resources, new string[] { string.Empty });
        }
        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, IEnumerable<string> resources, string action)
        {
            return GetAuthorizeResult(principal, resources, new string[] { action });
        }
        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions)
        {
            var items = ResourceItemCollection.CreateResourceItems(resources, actions, null);
            return GetAuthorizeResult(principal, items);
        }
        public AuthorizeResult GetAuthorizeResult(ResourceItemCollection items)
        {
            return GetAuthorizeResult(this.PrincipalManager.CurrentPrincipal, items);
        }
        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, ResourceItemCollection items)
        {
            return this.AuthorizationProvider.GetAuthorizeResult(principal, items);
        }

        #endregion
        #region GetAuthorizeResultAsync

        public async Task<AuthorizeResult> GetAuthorizeResultAsync(string resource, string action, string subAction)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, new string[] { resource }, new string[] { action }, new string[] { string.Empty });
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IEnumerable<string> resources, string action, string subAction)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, resources, new string[] { action }, new string[] { subAction });
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, resources, actions, subActions);
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, string resource, string action, string subAction)
        {
            return await GetAuthorizeResultAsync(principal, new string[] { resource }, new string[] { action }, new string[] { string.Empty });
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, IEnumerable<string> resources, string action, string subAction)
        {
            return await GetAuthorizeResultAsync(principal, resources, new string[] { action }, new string[] { subAction });
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await GetAuthorizeResultAsync(principal, resources, actions, subActions, null);
        }

        public async Task<AuthorizeResult> GetAuthorizeResultAsync(string resource, string action, string subAction, string resourceItemId)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, resource, action, subAction, resourceItemId);
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IEnumerable<string> resources, string action, string subAction, string resourceItemId)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, resources, action, subAction, resourceItemId);
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions, IEnumerable<string> resourceItemIds)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, resources, actions, subActions, resourceItemIds);
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, string resource, string action, string subAction, string resourceItemId)
        {
            return await GetAuthorizeResultAsync(principal, new string[] { resource }, new string[] { action }, new string[] { subAction }, new string[] { resourceItemId });
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, IEnumerable<string> resources, string action, string subAction, string resourceItemId)
        {
            return await GetAuthorizeResultAsync(principal, resources, new string[] { action }, new string[] { subAction }, new string[] { resourceItemId });
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions, IEnumerable<string> resourceItemIds)
        {
            var items = ResourceItemCollection.CreateResourceItems(resources, actions, subActions, resourceItemIds);
            return await GetAuthorizeResultAsync(principal, items);
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(ResourceItemCollection items)
        {
            return await GetAuthorizeResultAsync(this.PrincipalManager.CurrentPrincipal, items);
        }
        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, ResourceItemCollection items)
        {
            return await this.AuthorizationProvider.GetAuthorizeResultAsync(principal, items);
        }

        #endregion
        #region GetSystemResourceItemsAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public async Task<ResourceItemCollection> GetSystemResourceItemsAsync()
        {
            return await this.AuthorizationProvider.GetSystemResourceItemsAsync();
        }

        #endregion
        #region GetResourceItemsAsync

        public async Task<ResourceItemCollection> GetResourceItemsAsync()
        {
            return await GetResourceItemsAsync(this.PrincipalManager.CurrentPrincipal);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<ResourceItemCollection> GetResourceItemsAsync(IPrincipal principal)
        {
            return await this.AuthorizationProvider.GetResourceItemsAsync(principal);
        }

        #endregion
        #region IsAuthorized

        public bool IsAuthorized(string resource)
        {
            return IsAuthorized(this.PrincipalManager.CurrentPrincipal, resource);
        }
        public bool IsAuthorized(string resource, string action)
        {
            return IsAuthorized(this.PrincipalManager.CurrentPrincipal, resource, action);
        }
        public bool IsAuthorized(IEnumerable<string> resources)
        {
            return IsAuthorized(this.PrincipalManager.CurrentPrincipal, resources);
        }
        public bool IsAuthorized(IEnumerable<string> resources, string action)
        {
            return IsAuthorized(this.PrincipalManager.CurrentPrincipal, resources, action);
        }
        public bool IsAuthorized(IEnumerable<string> resources, IEnumerable<string> actions)
        {
            return IsAuthorized(this.PrincipalManager.CurrentPrincipal, resources, actions);
        }

        public bool IsAuthorized(IPrincipal principal, string resource)
        {
            return IsAuthorized(principal, new string[] { resource }, new string[] { string.Empty });
        }
        public bool IsAuthorized(IPrincipal principal, string resource, string action)
        {
            return IsAuthorized(principal, new string[] { resource }, new string[] { action });
        }
        public bool IsAuthorized(IPrincipal principal, IEnumerable<string> resources)
        {
            return IsAuthorized(principal, resources, new string[] { string.Empty });
        }
        public bool IsAuthorized(IPrincipal principal, IEnumerable<string> resources, string action)
        {
            return IsAuthorized(principal, resources, new string[] { action });
        }
        public bool IsAuthorized(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions)
        {
            var items = ResourceItemCollection.CreateResourceItems(resources, actions, null);
            return IsAuthorized(principal, items);
        }
        public bool IsAuthorized(ResourceItemCollection items)
        {
            return IsAuthorized(this.PrincipalManager.CurrentPrincipal, items);
        }
        public bool IsAuthorized(IPrincipal principal, ResourceItemCollection items)
        {
            var result = GetAuthorizeResult(principal, items);
            return result.IsAuthorized;
        }

        #endregion
        #region IsAuthorizedAsync

        public async Task<bool> IsAuthorizedAsync(Sidvall.Business.IDataPortalContext context)
        {
            if (context == null)
                throw new System.ArgumentNullException(nameof(context));

            this.DataPortalContextManager.EnsureIsValid(context);
            if (context.AffectedResources.Count == 0)
                context.InitializeAffectedResources(null);
            return await IsAuthorizedAsync(context.AffectedResources).ConfigureAwait(false);
        }

        public async Task<bool> IsAuthorizedAsync(string resource, string action, string subAction)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, new string[] { resource }, new string[] { action }, new string[] { string.Empty });
        }
        public async Task<bool> IsAuthorizedAsync(IEnumerable<string> resources, string action, string subAction)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, new string[] { action }, new string[] { subAction });
        }
        public async Task<bool> IsAuthorizedAsync(IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, actions, subActions);
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, string resource, string action, string subAction)
        {
            return await IsAuthorizedAsync(principal, new string[] { resource }, new string[] { action }, new string[] { string.Empty });
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, string action, string subAction)
        {
            return await IsAuthorizedAsync(principal, resources, new string[] { action }, new string[] { subAction });
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await IsAuthorizedAsync(principal, resources, actions, subActions, null);
        }

        public async Task<bool> IsAuthorizedAsync(string resource, string action, string subAction, string resourceItemId)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resource, action, subAction, resourceItemId);
        }
        public async Task<bool> IsAuthorizedAsync(IEnumerable<string> resources, string action, string subAction, string resourceItemId)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, action, subAction, resourceItemId);
        }
        public async Task<bool> IsAuthorizedAsync(IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions, IEnumerable<string> resourceItemIds)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, resources, actions, subActions, resourceItemIds);
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, string resource, string action, string subAction, string resourceItemId)
        {
            return await IsAuthorizedAsync(principal, new string[] { resource }, new string[] { action }, new string[] { subAction }, new string[] { resourceItemId });
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, string action, string subAction, string resourceItemId)
        {
            return await IsAuthorizedAsync(principal, resources, new string[] { action }, new string[] { subAction }, new string[] { resourceItemId });
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions, IEnumerable<string> resourceItemIds)
        {
            var items = ResourceItemCollection.CreateResourceItems(resources, actions, subActions, resourceItemIds);
            return await IsAuthorizedAsync(principal, items);
        }
        public async Task<bool> IsAuthorizedAsync(ResourceItemCollection items)
        {
            return await IsAuthorizedAsync(this.PrincipalManager.CurrentPrincipal, items);
        }
        public async Task<bool> IsAuthorizedAsync(IPrincipal principal, ResourceItemCollection items)
        {
            var result = await GetAuthorizeResultAsync(principal, items);
            return result.IsAuthorized;
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return "AuthorizationManager - IAuthorizationProvider";
        }

        #endregion

        #endregion
        #region Constructors

        public AuthorizationManager(Sidvall.Security.IAuthorizationProvider authorizationProvider)
            : this(authorizationProvider, null)
        {
        }
        public AuthorizationManager(Sidvall.Security.IAuthorizationProvider authorizationProvider, PrincipalManager principalManager)
        {
            this.AuthorizationProvider = authorizationProvider;
            this.PrincipalManager = principalManager;
            this.DataPortalContextManager = new Sidvall.Business.DataPortalContextManager();
        }

        #endregion
    }
}
